#
# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
#
# SPDX-License-Identifier: BSD-2-Clause
#

cmake_minimum_required(VERSION 3.7.2)
include_guard(GLOBAL)

# This takes a camkes produced dependency file (this means we can assume one dependency
# per line) and produces a cmake list of dependencies
function(MakefileDepsToList mdfile output_variable)
    file(READ "${mdfile}" raw_file)
    # First remove the target of the dependency list
    string(
        REGEX
        REPLACE
            "^[^:]*: \\\\\r?\n"
            ""
            string_deps
            "${raw_file}"
    )
    # Now turn the list of dependencies into a cmake list. We have assumed
    # that this makefile dep file was generated by camkes and so it has one
    # item per line
    string(
        REGEX
        REPLACE
            "\\\\\r?\n"
            ";"
            deps
            "${string_deps}"
    )
    # Strip the space from each dep
    foreach(dep IN LISTS deps)
        # Strip extra spacing
        string(STRIP "${dep}" dep)
        list(APPEND final_deps "${dep}")
    endforeach()
    # Write the output to the parent
    set("${output_variable}" "${final_deps}" PARENT_SCOPE)
endfunction(MakefileDepsToList)

# Wraps a call to execute_process with checks that only rerun execute_process
# if the command or input files are changed. It also uses a depfile to track
# any files that the command touches internally. This function currently won't
# work without a depfile.
macro(execute_process_with_stale_check invoc_file deps_file outfile extra_dependencies)
    # Check all input extra_dependencies are actual files with timestamps
    # We need to assign the input list variable to another variable so that it works with
    # foreach( IN LISTS ).  As the foreach LISTS variaint doesn't seem to work with marco arguments.
    set(_extra_dependencies ${extra_dependencies})
    foreach(dep IN LISTS _extra_dependencies)
        if(NOT (EXISTS "${dep}"))
            message(FATAL_ERROR "extra_dependencies: could not find file: ${dep}")
        endif()
    endforeach()

    # We need to determine if we actually need to regenerate. We start by assuming that we do
    set(regen TRUE)
    if((EXISTS "${invoc_file}") AND (EXISTS "${deps_file}") AND (EXISTS "${outfile}"))
        file(READ "${invoc_file}" old_contents)
        if("${old_contents}" STREQUAL "${ARGN}")
            MakefileDepsToList("${deps_file}" deps)
            # At this point assume we do not need to regenerate, unless we found a newer file
            set(regen FALSE)
            foreach(dep IN LISTS deps _extra_dependencies)
                if("${dep}" IS_NEWER_THAN "${outfile}")
                    set(regen TRUE)
                    break()
                endif()
            endforeach()
        endif()
    endif()
    if(regen)
        message(STATUS "${outfile} is out of date. Regenerating...")
        execute_process(${ARGN})
        file(WRITE "${invoc_file}" "${ARGN}")
    endif()
    # Add dependencies
    MakefileDepsToList("${deps_file}" deps)
    set_property(
        DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
        APPEND
        PROPERTY CMAKE_CONFIGURE_DEPENDS "${deps};${extra_dependencies}"
    )

endmacro(execute_process_with_stale_check)

# For custom commands that invoke other build systems, we can create a depfile
# based on a find traversal of the directory.  This saves using CMake to glob it
# which is much slower
macro(create_depfile_by_find ret outfile depfile dir)
    file(RELATIVE_PATH path ${CMAKE_BINARY_DIR} ${outfile})
    list(
        APPEND
            ${ret}
            COMMAND
            printf
            "${path}: "
            >
            ${depfile}
    )
    list(
        APPEND ${ret} COMMAND
        find
            -L
            ${dir}
            -type
            f
            -printf
            "%p "
            >>
            ${depfile}
    )
    list(APPEND ${ret} DEPFILE ${depfile})
endmacro()
